/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */

package io.iec.edp.caf.multicontext.context;

import io.iec.edp.caf.commons.event.StartupCompletedEvent;
import io.iec.edp.caf.multicontext.factory.ModuleBeanFactory;
import io.iec.edp.caf.multicontext.resolver.MultiContextResourceResolver;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.io.support.ResourcePatternResolver;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.Map;

/**
 * 模块上下文
 * 主要是暴露私有方法为public方法
 *
 * @author guowenchang
 */
@Slf4j
public class ModuleApplicationContext extends AnnotationConfigApplicationContext {

    public ModuleApplicationContext(DefaultListableBeanFactory moduleBeanFactory) {
        super(moduleBeanFactory);
    }

    @Override
    protected ResourcePatternResolver getResourcePatternResolver() {
        return new MultiContextResourceResolver(this);
    }

    public void prepareBeanFactory(String name) {
        long time = System.currentTimeMillis();

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);
        long cost = System.currentTimeMillis() - time;
//        System.out.println(name + " cost:" + cost + "ms");
    }

    public void postProcessBeanFactory() {
        super.postProcessBeanFactory(getBeanFactory());
    }

    public void invokeBeanFactoryPostProcessors() {
//        try{
            super.invokeBeanFactoryPostProcessors(getBeanFactory());
//        }catch (IllegalArgumentException e){
//            //spring应用启动的时，强制校验了这个spring.factories。但是并行启动，有的模块下所有的jar就是没有spring.factories
//            //没有spring.factories就是没有bean的申明
//            //其实这种jar也加载了，
//            if(e.getMessage().startsWith("No auto configuration classes found in META-INF/spring.factories")){
//
//            }
//            // 不服模块不含bean但没有归并到特殊模块中，invoke会报错，吞掉异常
//            log.warn("No Beans Warning, "+e.getMessage());
//        }
    }

    /**
     * 模块注册BeanPostProcessor后处理
     * //todo 这里临时解决了tcc的问题，但是postprocessor的注册是有顺序的，目前为了解决tcc的问题把这些processor统一注册到了最后可能有问题
     * @param platformBeanPostProcessors
     */
    public void afterRegisterBeanPostProcessors(List<BeanPostProcessor> platformBeanPostProcessors){
        var moduleBeanPostProcesser = ((ModuleBeanFactory)this.getBeanFactory()).getBeanPostProcessors();
        platformBeanPostProcessors.forEach(x->{
            var curBeanType = x.getClass().getName();
            //只注册igix自己（io.iec.edp,com.inpsur）和没有被注册的
            //只能通过beanFactory来注册，然后后面本身的registerBeanPostProcessors才会把这些beanFactory里的注册到moduleContext里
            if((!moduleBeanPostProcesser.stream().anyMatch(xx->xx.getClass().getName().equalsIgnoreCase(curBeanType)))
                    && (curBeanType.startsWith("io.iec.edp") ||curBeanType.startsWith("com.inspur"))){
                ((ModuleBeanFactory)this.getBeanFactory()).addBeanPostProcessor(x);
            }
//             || curBeanType.equalsIgnoreCase("org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator")
//            ((ModuleBeanFactory)this.getBeanFactory()).addBeanPostProcessor(x);
        });
    }

    public void registerBeanPostProcessors(List<BeanPostProcessor> platformBeanPostProcessors) {
//        try {
//            Field field = AbstractBeanFactory.class.getDeclaredField("beanPostProcessors");
//            field.setAccessible(true);
//            List<BeanPostProcessor> list = (List<BeanPostProcessor>)field.get((ModuleBeanFactory)this.getBeanFactory());
//            list.clear();
//
//            platformBeanPostProcessors.forEach(x->{
//                var curBeanType = x.getClass().getName();
////                只注册系统bean
//                if(!(curBeanType.startsWith("io.iec.edp") ||curBeanType.startsWith("com.inspur"))){
//                    ((ModuleBeanFactory)this.getBeanFactory()).addBeanPostProcessor(x);
//                }
//            });
//        } catch (NoSuchFieldException e) {
//            e.printStackTrace();
//        } catch (IllegalAccessException e) {
//            e.printStackTrace();
//        }
        super.registerBeanPostProcessors(getBeanFactory());
    }

    public void finishBeanFactoryInitialization() {
        try{
            super.finishBeanFactoryInitialization(getBeanFactory());
        }catch (IllegalStateException e){
            // 不服模块不含bean但没有归并到特殊模块中，initialize会报错，吞掉异常
            log.warn("No Beans Warning, "+e.getMessage());
        }

//        for (BeanFactoryPostProcessor beanFactoryPostProcessor : this.getBeanFactoryPostProcessors()) {
//            if (beanFactoryPostProcessor instanceof BeanCollectorBeanFactoryPostProcessor) {
//                ((BeanCollectorBeanFactoryPostProcessor) beanFactoryPostProcessor).instantiationBeanCollectors();
//            }
//        }
    }

    @Override
    public void initMessageSource() {
        super.initMessageSource();
    }

    @Override
    public void initApplicationEventMulticaster() {
        super.initApplicationEventMulticaster();
    }

    /**
     *
     */
    public void prepareRefresh() {
        super.prepareRefresh();
    }

    @Override
    public void onRefresh() {
        super.onRefresh();
    }

    @Override
    public void registerListeners() {
        super.registerListeners();
    }

    /**
     * 模块注册Listener后处理
     */
    //todo 这里最好是想办法让各个模块去触发自己的事件？这样把子模块的bean弄过来有污染
    public void afterRegisterListeners(PlatformApplicationContext platformContext){
        //特殊处理了下CAF的启动后事件，因为CAF的启动后事件是在PlatformApplicationContext里触发的。
        //但是子模块里注册的这个事件的Bean都不在PlatformApplicationContext里，所以这里把这些CAF启动后事件的bean识别出来注册到PlatformApplicationContext里
        var beanNmaes = this.getBeanNamesForType(ApplicationListener.class, true, false);
        var beans = this.getBeansOfType(ApplicationListener.class);
        for(var key: beans.keySet()){
            var interfaces = beans.get(key).getClass().getGenericInterfaces();
            for (var i: interfaces){
                if(i instanceof ParameterizedType){
                    var paramType = (ParameterizedType)i;
                    var tsource = paramType.getActualTypeArguments();
                    if(tsource.length>0 &&
                            (tsource[0].getTypeName().equalsIgnoreCase(StartupCompletedEvent.class.getTypeName())
                                    || tsource[0].getTypeName().equalsIgnoreCase("io.iec.edp.caf.boot.event.startup.StartupCompletedEvent"))){
                        platformContext.addApplicationListener(beans.get(key));
                    }
                }
            }
        }
    }

    @Override
    public void finishRefresh() {
        super.finishRefresh();
    }

    @Override
    public void destroyBeans() {
        super.destroyBeans();
    }

    @Override
    public void cancelRefresh(BeansException ex) {
        super.cancelRefresh(ex);
    }

    @Override
    public void resetCommonCaches() {
        super.resetCommonCaches();
    }

}
